/*
 * clog.c
 *
 *  Created on: 2012-7-22
 *      Author: lin
 */
#include <pthread.h>

#include "collection/hash_map.h"
#include "concurrent/concurrent.h"
#include "def/hash.h"
#include "def/defs.h"

#include "clog.h"
#include "clog_conf.h"
#include "clog_category.h"
#include "clog_thread_category.h"
#include "clog_thread_local.h"
#include "clog_level.h"
#include "clog_event.h"

#define Cat clog_category_t
#define TCat clog_thread_category_t
#define Conf clog_conf_t
#define Local clog_thread_local_t

static voliate int32_t _conf_seq_id = 0;
static uint32_t _category_id = 0;
static Conf *_conf = NULL;
static hash_map_t *_category_table = NULL;
static thread_key _thread_key;
static rowlock _envlock = rwlock_initializer;


int clog_load(const char *confpath) {
    int ok = 0;
    rwlock_wrlock(&_envlock);
    if (_conf_seq_id != 0) {
        goto exit;
    }

    Conf *conf = clog_conf_new(confpath);
    if (!conf) {
        ok = 1;
        goto exit;
    }

    uint32_t seq_id = _conf_seq_id;
    ++seq_id;
    clog_conf_set_seq_id(conf, seq_id);
    _conf = conf;

    thread_key_create(&_thread_key);
    hash_map_t * table = hash_map_new();
    hash_map_set_hash_fn(table, mixed_t_hash_string);
    hash_map_set_comp_key_fn(table, mixed_t_cmpare_string);
    hash_map_set_free_val_fn(table, clog_category_free);
    _category_table = table;

    mb_sync();
    _conf_seq_id = seq_id;

    exit:
    rwlock_unlock(&_envlock);
    return ok;
}
int clog_reload(const char *confpath);
void clog_destory(void);

Cat *clog_get_category(const char *cname) {
    Cat *category;
    rwlock_wrlock(&_envlock);
    if (_conf_seq_id == 0) {
        category = NULL;
        goto exit;
    }
    mixed_t key, val;
    key.ptr = cname;

    if (hash_map_get(_category_table, key, &val) == HASH_MAP_EXIST) {
        category = val.ptr;
    } else {
        category = clog_category_new(cname,_category_id++);
        key.ptr = clog_category_get_name(category);
        val.ptr = category;
        hash_map_put_absent(_category_table, key, val);
    }
    exit:
    rwlock_unlock(&_envlock);
    return category;
}
static Conf *_conf_dup() {
    Conf *conf;
    rwlock_rdlock(&_envlock);
    conf = clog_conf_dup(_conf);
    if (!conf) {
        errors("copy clog conf");
    }
    rwlock_unlock(&_envlock);
    return conf;
}

static TCat _thread_category_new(const char *name,Conf *conf){
    return NULL;
}
void clog(const clog_category_t * category, const char *fname, uint16_t fnamelen,
        const char *func, uint16_t funclen, uint32_t line, uint16_t level,
        const char *format, ...){
    assert(level<=CLOG_MAX_LEVEL);
    Local *local = thread_key_get(_thread_key);
    Conf *conf;
    if (!local) {
        conf = _conf_dup();
        local = clog_thread_local_new(conf);
        thread_key_set(_thread_key, local);
    }
    if (conf->seq_id != _conf_seq_id) {
        conf = _conf_dup();
        clog_thread_local_update(local,conf);
    }else{
        conf= clog_thread_local_get_conf(local);
    }
    TCat cat = clog_thread_local_get_tcategory(category);
    if(!clog_thread_category_logable(cat,level)){
        return;
    }

    clog_event_t event;
    clog_event_set(&event,category->name,category->name_len,fname,fnamelen,func,funclen,line,level);
    va_list args;
    va_start(args, format);
    clog_event_set_format(&event,format,args);


    va_end(args);
}

#undef Cat
#undef TCat
#undef Conf
#undef Local
